﻿
package extremefx.storage {
	import extremefx.IDisposable;
	import extremefx.events.EventArgs;
	import extremefx.events.EventHandler;
	import extremefx.storage.QeuedItem;		

	/**
	 * @author Marcelo Volmaro
	 */
	 
	public final class LoadQeue implements IDisposable {
		private var _maxConc:uint;
		private var _qeue:Vector.<QeuedItem>;
		private var _slots:Vector.<QeuedItem>;
		private var _isLoading:Boolean;
		private var _maxItems:uint;
//		private var _concItems:uint;
		
		/**
		 * This class is used to manage resource loading.
		 */
		
		public function LoadQeue() {
			_qeue = new Vector.<QeuedItem>();
			_slots = new Vector.<QeuedItem>();
			_isLoading = false;
			_maxItems = 0; 
//			_concItems = 0;
		}
		
		/**
		 * Starts the resource loading.
		 * @param pMax maximum number of connections at the same time.
		 */
		public function start(pMax:uint = 2):void{
			_maxConc = pMax;
			if (!_isLoading) {
				if (_eOnQeueStart) _eOnQeueStart.fire();
				getNextFile();
			}
		}
		
		/**
		 * Adds a resource to the load qeue.
		 * @param pFrom [String|URLRequest] a string or an URLRequest pointing to the resource on the server.
		 * @param pType a <code>QeuedItemType</code> of the resource. If nothing is passed (null or empty string) the class will try to guess the type by its extension.
		 * @param pContext the Context in wich the resource will be loaded.
		 * @return The <code>QeuedItem</code> assigned to the task of loading the resource. 
		 */
		public function addLoader(pItem:QeuedItem, pContext:Object = null):QeuedItem {
			if (_isQueued(pItem) == -1) _maxItems = _qeue.push(pItem);
			return pItem;
		}
		
		private var _disposing:Boolean;

		/**
		 * Forces an item to be loaded after the first loading element on the qeue finishes loading.
		 * @param pForce a <code>QeuedItem</code> you want to force loading. The <code>QeuedItem</code> must be on the qeue.
		 * @return true if successful, false if not.
		 */
		public function force(pItem:QeuedItem):Boolean{
			var pos:int = _isQueued(pItem);
			
			if (pos == -1) return false;
			
			var forced:Object = _qeue[pos];
			_qeue.splice(pos, 1);
			_qeue.unshift(forced);
			return true;
		}
		
		private function _isQueued(pItem:QeuedItem):int {
			var i:uint = 0;
			for each(var qi:QeuedItem in _qeue){
				if (qi.equals(pItem)) return i;
				++i;
			}
			
			return -1;
		}
		
		/**
		 * Cancel the loading of all items. Also, clears the qeue.
		 */
		public function cancelAll():void{
			_maxItems = 0;//_concItems = 0;
			var q:Vector.<QeuedItem> = _qeue;
			_qeue = new Vector.<QeuedItem>();
			var i:uint = 0;
			var qi:QeuedItem;
			
			while (qi = q[i++]) qi.stop();
			
			q = _slots;
			_slots = new Vector.<QeuedItem>();
			
			while (qi = q[i++]) qi.stop();
			_isLoading = false;
		}
		
		/**
		 * Gets the total number of qeued items
		 * @return the number of items in the qeue
		 */
		public function get count():uint{
			return _maxItems;
		}
		
		private function getNextFile():void{
			if (_qeue.length == 0 && _slots.length == 0){
				_isLoading = false;
				if (_eOnQeueComplete) _eOnQeueComplete.fire();
				
			} else if (_qeue.length != 0 && _slots.length < _maxConc) {
				_isLoading = true;
				var cq:QeuedItem = _qeue.shift();
				cq.onComplete.add(_done);
				
				_slots.push(cq);
				
				cq.start();
				getNextFile();
			}
		}
		
		private function _done(pSender:QeuedItem, pArgs:EventArgs):void{
			if (_eOnItemComplete) _eOnItemComplete.fire(new QeuedItemLoadedEventArgs(pSender));
			_slots.splice(_slots.indexOf(pSender), 1);
			getNextFile();
		}
		
		private var _eOnItemComplete:EventHandler;
		public function get onItemComplete():EventHandler{
			if (_eOnItemComplete == null) _eOnItemComplete = new EventHandler(this);
			return _eOnItemComplete;
		}
		
		private var _eOnQeueStart:EventHandler;
		/**
		 * Event. Fires when the qeue starts loading resources
		 */
		public function get onQeueStart():EventHandler{
			if (_eOnQeueStart == null) _eOnQeueStart = new EventHandler(this);
			return _eOnQeueStart;
		}
			
		private var _eOnQeueComplete:EventHandler;
		/**
		 * Event. Fires when the qeue has finished loading all the resources.
		 */
		public function get onQeueComplete():EventHandler{
			if (_eOnQeueComplete == null) _eOnQeueComplete = new EventHandler(this);
			return _eOnQeueComplete;
		}
		
		public function dispose():void {
			if (_disposing) return;
			_disposing = true;

			var i:uint = 0;
			var qi:QeuedItem;
			
			while (qi = _qeue[i++]) qi.dispose();
			_qeue = null;

			while (qi = _slots[i++]) qi.dispose();
			_slots = null;
			_eOnQeueStart = _eOnQeueComplete = null;
		}
	}
}
